/* Copyright 2001,2002,2003 NAH6 BV
 * All Rights Reserved
 *
 *  $Header: /var/lib/cvs/secphone/ui/windows/nhThread.cpp,v 1.10 2003/11/21 16:12:58 itsme Exp $
 *
 *
 *  this handles system level stuff to create a thread.
 *
 *  classes that want to have a thread, should subclass this
 *  class and override the 'ThreadProc' method.
 *
 */

#include <windows.h>
#include "debug.h"

#include "nhThread.h"

void DumpThreadTimes(HANDLE hThread);

NHThread::NHThread()
{
    debug("NHThread-contructor\n");

    m_bStarted= false;
    m_bTerminated= false;

    m_hStartEvent= CreateEvent(NULL, FALSE, FALSE, NULL);
    if (m_hStartEvent==INVALID_HANDLE_VALUE)
        error("ERROR creating NHthread start event\n");

    m_hThread= CreateThread(NULL, 0, 
            (LPTHREAD_START_ROUTINE)MainThreadProc, 
            this, 0, &m_nThreadId);
    if (m_hThread==INVALID_HANDLE_VALUE || m_hThread==NULL)
    {
        error("ERROR creating NHthread\n");

        m_hThread=INVALID_HANDLE_VALUE;
    }

    debug("new thread: id=%08lx h=%08lx\n", m_nThreadId, m_hThread);

    debug("NHThread-contructor  END\n");
}

NHThread::~NHThread()
{
    DWORD nExitCode;

    debug("NHThread-destructor %08lx  %08lx\n", m_nThreadId, m_hThread);
    if (m_hThread!=INVALID_HANDLE_VALUE) {

        if (!GetExitCodeThread(m_hThread, &nExitCode))
            error("ERROR in GetExitCodeThread\n");
        if (!TerminateThread(m_hThread, nExitCode))
            error("ERROR in TerminateThread\n");
        DumpThreadTimes(m_hThread);
        if (!CloseHandle(m_hThread))
            error("ERROR closing NHthread handle\n");
    }
    if (m_hStartEvent!=INVALID_HANDLE_VALUE
        && !CloseHandle(m_hStartEvent))
        error("ERROR closing startevent handle\n");

    debug("NHThread-destructor END\n");
}

void NHThread::StartThread(HANDLE hEvStarted /* = INVALID_HANDLE_VALUE */)
{
    m_hEventStarted= hEvStarted;

    if (!SetEvent(m_hStartEvent))
        error("ERROR starting NHthread\n");
}

bool NHThread::Started() const
{
    return m_bStarted;
}
bool NHThread::Terminated() const
{
    return m_bTerminated;
}
bool NHThread::Running() const
{
    return Started() && !Terminated();
}
HANDLE NHThread::Handle() const
{
    return m_hThread;
}

DWORD NHThread::MainThreadProc(NHThread *t)
{
    return t->ThreadObjectProc();
}

DWORD NHThread::ThreadObjectProc()
{
    DWORD rc=0;

    //   the virtual function table is not initialized
    //   before the constructor has finished executing!
    //   so cannot use any virtual methods until after the
    //   wait for start event.
    debug("NHThread::ThreadObjectProc %08lx waiting for start\n", m_nThreadId);

    bool bOk= (WaitForSingleObject(m_hStartEvent, INFINITE)==WAIT_OBJECT_0);
    CloseHandle(m_hStartEvent);
    m_hStartEvent= INVALID_HANDLE_VALUE;

    if (m_hEventStarted!=INVALID_HANDLE_VALUE 
            && !SetEvent(m_hEventStarted))
    {
        error("NHThread::ThreadObjectProc ERROR signalling start of NHthread\n");
    }

    if (bOk)
    {
        debug("NHThread::ThreadObjectProc %08lx - %hs started\n", m_nThreadId, name());

        m_bStarted= true;
        rc= ThreadProc();

        debug("NHThread::ThreadObjectProc %08lx - %hs terminated\n", m_nThreadId, name());
    }
    else {
        debug("NHThread::ThreadObjectProc ERROR: thread %hs failed to start\n", name());
    }

    m_bTerminated= true;

    return rc;
}

BOOL CalcFiletimeDiff(const FILETIME *tFirst, const FILETIME *tLast, FILETIME *tDiff);
DWORD CalcPromile(FILETIME *tA, FILETIME *tB);

void DumpThreadTimes(HANDLE hThread)
{
    FILETIME tCreate, tExit, tKernel, tUser;
    if (!GetThreadTimes(hThread, &tCreate, &tExit, &tKernel, &tUser))
    {
        error("GetThreadTimes");
        return;
    }

    FILETIME tDiff;
    CalcFiletimeDiff(&tCreate, &tExit, &tDiff);

    debug("      cr= %08lx%08lx  ex=%08lx%08lx  k=%08lx%08lx  u=%08lx%08lx  diff=%08lx%08lx   kcpu=%4d  ucpu=%4d\n",
        tCreate.dwHighDateTime, tCreate.dwLowDateTime,
        tExit.dwHighDateTime, tExit.dwLowDateTime,
        tKernel.dwHighDateTime, tKernel.dwLowDateTime,
        tUser.dwHighDateTime, tUser.dwLowDateTime,
        tDiff.dwHighDateTime, tDiff.dwLowDateTime,
        CalcPromile(&tKernel, &tDiff), CalcPromile(&tUser, &tDiff) );
}

BOOL CalcFiletimeDiff(const FILETIME *tFirst, const FILETIME *tLast, FILETIME *tDiff)
{
    /*
    int carryLow= (tLast->dwLowDateTime < tFirst->dwLowDateTime) ? 1 : 0;
    tDiff->dwLowDateTime= tLast->dwLowDateTime-tFirst->dwLowDateTime;

    int carryHigh= (tLast->dwHighDateTime < tFirst->dwHighDateTime+carryLow) ? 1 : 0;
    tDiff->dwHighDateTime= tLast->dwHighDateTime-tFirst->dwHighDateTime-carryLow;

    return carryHigh!=0;
    */
    __int64 a= tFirst->dwHighDateTime; a<<=32; a |= tFirst->dwLowDateTime;
    __int64 b= tLast->dwHighDateTime; b<<=32; b |= tLast->dwLowDateTime;

    __int64 c= b-a;

    tDiff->dwHighDateTime= (DWORD) (c>>32);
    tDiff->dwLowDateTime= (DWORD) (c);

    return (BOOL)(c>>32);
}

DWORD CalcPromile(FILETIME *tA, FILETIME *tB)
{
    /*
    int n=0;

    DWORD a;
    
    if (tA->dwHighDateTime >> 16) {
        a= tA->dwHighDateTime;
    }
    else {
        a= (tA->dwHighDateTime << 16) | (tA->dwLowDateTime >> 16);
        n-=16;
    }

    DWORD b;
    
    if (tB->dwHighDateTime >> 16) {
        b= tB->dwHighDateTime;
    }
    else {
        b= (tB->dwHighDateTime << 16) | (tB->dwLowDateTime >> 16);
        n+=16;
    }

    if (b==0)
        return 0xffffffffU;

    return ((1000*a)/b)<<n;

  */
    __int64 a= tA->dwHighDateTime; a<<=32; a |= tA->dwLowDateTime;
    __int64 b= tB->dwHighDateTime; b<<=32; b |= tB->dwLowDateTime;

    if (b==0)
        return -1;
    __int64 c= a/b;
    c *= 1000;

    return (DWORD)c;
}
